import random
import math
import pyglet
from vec2d import Vec2d
import data
import particle


class Debris:
    
    def __init__(self, game):
        self.to_disable = set()
        self.active_sprites = set()
        self.disabled_sprites = set()
        self.batch = pyglet.graphics.Batch()
        self.draw = self.batch.draw
        self.game = game

    def update(self, dt):
        """Update active debris objects"""
        if self.to_disable:
            self.active_sprites -= self.to_disable
            self.disabled_sprites |= self.to_disable
            self.to_disable.clear()
        for sprite in self.active_sprites:
            sprite.update(dt)

    def add(self, sprite):
        self.active_sprites.add(sprite)
        sprite.batch = self.batch
        self.game.collision_system.add(sprite)

    def remove(self, sprite):
        if sprite in self.active_sprites:
            self.active_sprites.remove(sprite)
            #self.game.collision_system.remove(sprite)
            sprite.delete()

    def disable(self, sprite):
        """Remove a sprite from updates, but still draw it"""
        self.to_disable.add(sprite)
        #self.game.collision_system.remove(sprite)

    def query(self, query_obj):
        """Test if query_obj collides with any debris,
        return a generator yielding the debris that collide.

        query_obj must implement the CollisionGeometry interface
        """
        #print "I get into debris query"
        self.last_query_obj = query_obj # XXX for debugging
        query_position = query_obj.collision_position
        query_radius = query_obj.collision_radius
        for obj in tuple(self.active_sprites):
            if obj is not query_obj:
                distance = (query_position - obj.collision_position).get_length()
                if distance <= query_radius + obj.collision_radius:
                    yield obj

    def collide(self, query_obj):
        """Test if query_obj collides with any objects in the collision system,
        for each collision, call the on_collide method for both objects

        query_obj must implement the CollisionGeometry interface
        """
        #print "I get into debris collide"
        for collided_obj in self.query(query_obj):
            query_obj.on_collide(self, collided_obj)
            collided_obj.on_collide(self, query_obj)
            #print "boom"


class DebrisSprite(pyglet.sprite.Sprite):
    """Random litter tossed by the trawler"""

    FLOAT_TIME = 10 # seconds to float on the surface
    SINK_SPEED = 50
    SINK_DEVIATION = 20
    LAUNCH_SPEED = 300
    LAUNCH_ANGLE = 70
    ROT_SPEED = 200
    ROT_DEVIATION = 50
    FLYING_DAMPING = 0.9
    FLOATING_DAMPING = 0.97
    SINKING_DAMPING = 0.4
    SPLASH_ROTATION_DAMPING = 0.2
    GRAVITY = -400
    FLOATATION = 800

    ## Debris States ##
    FLYING_STATE = 0
    FLOATING_STATE = 1
    SINKING_STATE = 2
    PILED_STATE = 3

    IMAGES = (
        data.image('tire.png'),
    )

    SPLASH_SOUNDS = (
        data.sound('splash1.wav'),
        data.sound('splash2.wav'),
        data.sound('splash3.wav'),
        data.sound('splash4.wav'),
        data.sound('splash5.wav'),
        data.sound('splash6.wav'),
    )

    SINKING_SOUNDS = (
        data.sound('sinking1.mp3'),
        data.sound('sinking2.mp3'),
        data.sound('sinking3.mp3'),
        data.sound('sinking4.mp3'),
        data.sound('sinking5.mp3'),
        data.sound('sinking6.mp3'),
        data.sound('sinking7.mp3'),
        data.sound('sinking8.mp3'),
        data.sound('sinking9.mp3'),
    )

    def __init__(self, game, posx, posy, direction):
        image = random.choice(self.IMAGES)
        image.anchor_x = image.width / 2
        image.anchor_y = image.height / 2
        pyglet.sprite.Sprite.__init__(self, image, posx, posy)
        self.rotation = random.randint(0, 359)
        self.debris = game.debris
        self.debris.add(self)
        self.state = self.FLYING_STATE
        self.velocity = Vec2d(self.LAUNCH_SPEED * direction, 0)
        self.velocity.rotate(self.LAUNCH_ANGLE * direction)
        self.rot_speed = random.gauss(self.ROT_SPEED, self.ROT_DEVIATION) * random.choice((1, -1))
        self.water_level = game.water_level
        self.radius = (image.width + image.height) / 4.0
        self.collision_radius = self.radius
        self.damping = self.FLYING_DAMPING
        self.float_timer = 0
        self.sea_floor = game.sea_floor

    def move(self, dt):
        """Update position, velocity and rotation"""
        self.velocity *= self.damping
        self.velocity.y += self.GRAVITY * dt
        self.set_position(self.x + self.velocity.x * dt, self.y + self.velocity.y * dt)
        self.rotation += self.rot_speed * dt

    def update_flying(self, dt):
        """Update when in the flying state"""
        self.move(dt)
        if self.y < self.water_level:
            # Hit the water
            self.rot_speed *= self.SPLASH_ROTATION_DAMPING
            self.state = self.FLOATING_STATE
            self.damping = self.FLOATING_DAMPING
            self.float_timer = self.FLOAT_TIME
            random.choice(self.SPLASH_SOUNDS).play()
            particle.splash_effect.splash(self.collision_position, 
                particles=100, velocity=180, spread=18, size=50)

    def update_floating(self, dt):
        """Update when floating"""
        self.float_timer -= dt
        if self.float_timer > 0:
            if self.y < self.water_level:
                below_water = (self.water_level - self.y) / self.radius
                self.velocity.y += (self.FLOATATION * below_water)**1.2 * dt
            self.move(dt)
        else:
            # Time to sink
            self.state = self.SINKING_STATE
            random.choice(self.SINKING_SOUNDS).play()
            self.bubble_emitter = particle.BubbleEmitter(self, speed=0,
                rate=50, spread=15,
                sizes=[(50, 50), (30, 30), (40,40), (22,22), (17,17), (20,20)])

    def update_sinking(self, dt):
        """Update when sinking"""
        self.velocity *= self.SINKING_DAMPING
        self.velocity.y += self.GRAVITY * dt
        self.move(dt)
        self.bubble_emitter.update(dt)
        if self.sea_floor.collision_point(self, hardness=0.6) is not None:
            self.state = self.PILED_STATE
            self.sea_floor.pile_sprite(self)
            self.bubble_emitter.kill()
            del self.bubble_emitter
            self.debris.disable(self) # don't update, just draw

    def update(self, dt):
        if self.state == self.FLYING_STATE:
            self.update_flying(dt)
        elif self.state == self.FLOATING_STATE:
            self.update_floating(dt)
        elif self.state == self.SINKING_STATE:
            self.update_sinking(dt)
        else:
            print "invalid state for debris!:", self.state
    
    @property
    def collision_position(self):
        return Vec2d(self.x, self.y)

    def on_collide(self, system, other):
        """Sumpthin hit us"""
        #print "Collide"
        #system.remove(self)
        pass
